﻿using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using SharedKernel.Infrastructure.Comm;
using SharedKernel.Infrastructure.Comm.Helpers;
using SharedKernel.Infrastructure.Grpc.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SharedKernel.Infrastructure.Grpc.Interceptors
{
    /// <summary>
    /// 自定义的Grpc客户端授权拦截器
    /// </summary>
    public class GrpcClientAuthorizationInterceptor:Interceptor
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IUserContext _userContext;
        private readonly IConfiguration _configuration;
        public GrpcClientAuthorizationInterceptor(IHttpContextAccessor httpContextAccessor, IUserContext userContext, IConfiguration configuration)
        { 
            _httpContextAccessor=httpContextAccessor;
            _userContext=userContext;
            _configuration=configuration;
        }
         
        /// <summary>
        /// 拦截客户端->服务端（单向请求）
        /// </summary>
        /// <typeparam name="TRequest"></typeparam>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="request"></param>
        /// <param name="context"></param>
        /// <param name="continuation"></param>
        /// <returns></returns>
        /// <exception cref="RpcException"></exception>
        public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
        {
            //从http请求头中获取Authorization，注意大小写
            if (_httpContextAccessor.HttpContext?.Request.Headers.TryGetValue("Authorization", out var authHeader) ?? false)
            {
                //这里面的key都是小写的，需要注意
                var authorizationCodeEntity = context.Options.Headers?.Get("authorizationcode");
                if (authorizationCodeEntity == null)
                {
                    throw new RpcException(new Status(StatusCode.Unauthenticated, "当前未设置加密秘钥"));
                }
                var authorizationCode = authorizationCodeEntity.Value.ToString();
                if (string.IsNullOrWhiteSpace(authorizationCode))
                {
                    throw new RpcException(new Status(StatusCode.Unauthenticated, "加密秘钥无效"));
                }

                var serNameEntity = context.Options.Headers?.Get("servicename");
                if (serNameEntity == null)
                {
                    throw new RpcException(new Status(StatusCode.Unauthenticated, "请求微服务不存在"));
                }
                var serviceName = serNameEntity.Value.ToString();
                if (string.IsNullOrWhiteSpace(serviceName))
                {
                    throw new RpcException(new Status(StatusCode.Unauthenticated, "请求微服务不存在：null"));
                }

                var clientOptions = _configuration.GetSection($"GrpcClients:{serviceName}").Get<GrpcClientOptions>();
                if (clientOptions == null||string.IsNullOrWhiteSpace(clientOptions.AuthorizationCode))
                {
                    throw new RpcException(new Status(StatusCode.Unauthenticated, $"请求微服务配置信息丢失"));
                }

                if (clientOptions.AuthorizationCode!= authorizationCode)
                {
                    throw new RpcException(new Status(StatusCode.Unauthenticated, $"加密秘钥错误"));
                }

                //加上秘钥加密后的SM3值
                string sm3 = BouncyCastleHelper.SM3Hash(authHeader+authorizationCode);
                context.Options.Headers.Add("sm3", sm3);
                //移除原来的AuthorizationCode
                context.Options.Headers.Remove(authorizationCodeEntity);
                //加入Jwt的Authorization头。设置为小写
                context.Options.Headers.Add("authorization", authHeader);

                return base.BlockingUnaryCall(request, context, continuation);
            }

            throw new RpcException(new Status(StatusCode.Unauthenticated, "Token无效"));
        }

    }
}
